package org.dylan.common.dao.hibernate;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import javacommon.xsqlbuilder.SafeSqlProcesser;
import javacommon.xsqlbuilder.SafeSqlProcesserFactory;
import javacommon.xsqlbuilder.XsqlBuilder;
import javacommon.xsqlbuilder.XsqlBuilder.XsqlFilterResult;
import javacommon.xsqlbuilder.safesql.DirectReturnSafeSqlProcesser;

import org.dylan.common.dao.BaseDao;
import org.dylan.common.model.AndQueryParam;
import org.dylan.common.model.BaseQueryParam;
import org.dylan.common.model.BaseQueryParam.BetweenValue;
import org.dylan.common.model.OrQueryParam;
import org.dylan.common.model.OrderBy;
import org.dylan.common.model.OrderBy.Direction;
import org.dylan.common.model.QueryParam;
import org.dylan.common.model.QueryRequest;
import org.dylan.common.util.XSqlUtils;
import org.dylan.core.util.Reflections;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.hibernate.dialect.Dialect;
import org.hibernate.internal.SessionFactoryImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

/**
 * 
 * @author dylan
 * 
 * @param <T>
 * @param <PK>
 */
@Repository
public class HibernateBaseDao<T, PK extends Serializable> implements
		BaseDao<T, PK> {
	
	/**
	 * 获取实体工厂管理对象
	 */
	@PersistenceContext
	private EntityManager entityManager;
	
	public EntityManager getEntityManager() {
		return entityManager;
	}
	
	protected Class clazz;

	public HibernateBaseDao() {
		setClazz(Reflections.getClassGenricType(getClass()));
	}

	protected void setClazz(Class clazz) {
		this.clazz = clazz;
	}

	public Session getSession() {
		// 事务必须是开启的(Required)，否则获取不到
		return (Session) getEntityManager().getDelegate();
	}

//	@Override
//	public void save(T entity) {
//		getSession().saveOrUpdate(entity);
//	}
//
//	@Override
//	public void remove(T entity) {
//		getSession().delete(entity);
//	}
//
//	@Override
//	public void remove(PK id) {
//		getSession().delete(findById(id));
//
//	}
//
//	@Override
//	public T findById(PK id) {
//		return (T) getSession().get(clazz, id);
//	}
//
//	@Override
//	public List<T> get(Collection<PK> ids) {
//		return null;
//	}
//
//	@Override
//	public List<T> getAll() {
//		return createCriteria().list();
//	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> findByProperty(String propertyName, Object value) {
		Criteria criteria = createCriteria();
		criteria.add(Restrictions.eq(propertyName, value));
		return criteria.list();
	}

	@Override
	public T findUniqueBy(String propertyName, Object value) {
		List<T> list = findByProperty(propertyName, value);
		return list == null ? null : list.get(0);
	}

	protected Criteria createCriteria() {
		return getSession().createCriteria(clazz);
	}

	/**
	 * 
	 * @param queryRequest
	 * @return
	 */
	protected Criteria createCriteria(QueryRequest queryRequest) {
		return createCriteria(queryRequest, true);
	}

	/**
	 * 
	 * @param request
	 * @param withOrder
	 * @return
	 */
	protected Criteria createCriteria(QueryRequest queryRequest,
			boolean withOrder) {
		Criteria criteria = createCriteria();
		for (QueryParam param : queryRequest.params()) {
			Criterion criterion = buildCriterion(param);
			if (criterion != null) {
				criteria.add(criterion);
			}
		}
		if (withOrder) {
			for (OrderBy order : queryRequest.orders()) {
				criteria.addOrder(order.order() == Direction.ASC ? Order
						.asc(order.field()) : Order.desc(order.field()));
			}
		}
		return criteria;
	}

	@Override
	public List<T> findByParam(QueryParam queryParam) {
		return findByParam(queryParam, null);
	}

	@Override
	public List<T> findByParam(QueryParam queryParam, OrderBy order) {
		QueryRequest request = new QueryRequest();
		if (queryParam != null) {
			request.addParam(queryParam);
		}
		if (order != null) {
			request.addOrderBy(order);
		}
		return findByParam(request);
	}

	@Override
	public List<T> findByParam(QueryRequest queryRequest) {
		Criteria criteria = createCriteria(queryRequest);
		return criteria.list();
	}

	/**
	 * 将查询的请求参数转换为hibernate可识别的Criterion对象
	 * 
	 * @param param
	 * @return
	 */
	protected Criterion buildCriterion(final QueryParam param) {
		if (param instanceof BaseQueryParam) {
			BaseQueryParam baseParam = (BaseQueryParam) param;
			if (!baseParam.shouldEscape()) {
				String field = baseParam.field();
				Object value = baseParam.value();
				switch (baseParam.matchType()) {
				case EQ:
					return Restrictions.eq(field, value);
				case NE:
					return Restrictions.ne(field, value);
				case LIKE:
					return Restrictions.like(field, value);
				case LIKE_LEFT:
					return Restrictions.like(field,
							value == null ? "" : value.toString(),
							MatchMode.END);
				case LIKE_RIGHT:
					return Restrictions.like(field,
							value == null ? "" : value.toString(),
							MatchMode.START);
				case LIKE_ANYWHERE:
					return Restrictions.like(field,
							value == null ? "" : value.toString(),
							MatchMode.ANYWHERE);
				case GT:
					return Restrictions.gt(field, value);
				case GE:
					return Restrictions.ge(field, value);
				case LT:
					return Restrictions.lt(field, value);
				case LE:
					return Restrictions.le(field, value);
				case BETWEEN:
					BetweenValue bv = (BetweenValue) value;
					return Restrictions.between(field, bv.min(), bv.max());
				case IN:
					if (value instanceof Collection) {
						return Restrictions.in(field, (Collection) value);
					} else {
						return Restrictions.in(field, (Object[]) value);
					}
				case NULL:
					return Restrictions.isNull(field);
				case NOT_NULL:
					return Restrictions.isNotNull(field);
				}
			}
		} else if (param instanceof AndQueryParam) {
			List<Criterion> list = new ArrayList<Criterion>();
			for (QueryParam p : ((AndQueryParam) param).params()) {
				Criterion cri = buildCriterion(p);
				if (cri != null) {
					list.add(cri);
				}
			}
			if (list.size() > 0) {
				return Restrictions
						.and(list.toArray(new Criterion[list.size()]));
			}
		} else if (param instanceof OrQueryParam) {
			List<Criterion> list = new ArrayList<Criterion>();
			for (QueryParam p : ((OrQueryParam) param).params()) {
				Criterion cri = buildCriterion(p);
				if (cri != null) {
					list.add(cri);
				}
			}
			if (list.size() > 0) {
				return Restrictions
						.or(list.toArray(new Criterion[list.size()]));
			}
		}
		return null;
	}

	public <E> List<E> findByHQL(String hql){
		return getSession().createQuery(hql).list();
	}
	
	public <E> List<E> findByHQL(String hql, QueryRequest queryRequest) {
		XsqlFilterResult queryXsqlResult = XSqlUtils.buildXsqlFilterResult(getXsqlBuilder(), hql, queryRequest);
		Query query = setQueryParameters(
				getSession().createQuery(queryXsqlResult.getXsql()),
				queryXsqlResult.getAcceptedFilters());
		return query.list();
	}
	

	protected Query setQueryParameters(Query q, Map<String,Object> params) {
		for (Map.Entry<String,Object> entry : params.entrySet()) {
			q.setParameter( entry.getKey(), entry.getValue());
		}
		return q;
	}
	
	protected XsqlBuilder getXsqlBuilder() {
		SessionFactoryImpl sf = (SessionFactoryImpl) (getSession().getSessionFactory());
		Dialect dialect = sf.getDialect();

		// or SafeSqlProcesserFactory.getMysql();
		SafeSqlProcesser safeSqlProcesser = SafeSqlProcesserFactory
				.getFromCacheByHibernateDialect(dialect);
		XsqlBuilder builder = new XsqlBuilder(safeSqlProcesser);

		if (builder.getSafeSqlProcesser().getClass() == DirectReturnSafeSqlProcesser.class) {
			System.err
					.println(HibernateBaseDao.class.getSimpleName()
							+ ".getXsqlBuilder(): 故意报错,你未开启Sql安全过滤,单引号等转义字符在拼接sql时需要转义,不然会导致Sql注入攻击的安全问题，请修改源码使用new XsqlBuilder(SafeSqlProcesserFactory.getDataBaseName())开启安全过滤");
		}
		return builder;
	}

}
